package com.hww.common.util;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;

import com.hww.common.util.constant.MemoryConstants;
import com.hww.common.util.constant.TimeConstants;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

/**
 * 各类型数据转换的实用方法
 */
public final class ConvertUtils {

    private static final int BUFFER_SIZE = 8192;
    private static final char[] HEX_DIGITS_UPPER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    private static final char[] HEX_DIGITS_LOWER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private ConvertUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * 将整数转换为十六进制字符串。
     * <p>
     * 该方法的目的是为了提供一种将整数类型数据转换为十六进制表示形式的手段。
     * 十六进制表示是一种常见的计算机中表示二进制数据的方式，它每四位二进制数对应一位十六进制数，
     * 使用0-9和A-F共16个符号进行表示，易于人类阅读和编写。
     *
     * @param num 需要转换的整数。
     * @return 返回表示num的十六进制字符串。
     */
    public static String int2HexString(int num) {
        return Integer.toHexString(num);
    }

    /**
     * 十六进制字符串转整数.
     *
     * @param hexString 十六进制字符串.
     * @return 转换后的整数
     */
    public static int hexString2Int(String hexString) {
        return Integer.parseInt(hexString, 16);
    }

    /**
     * 将字节数组转换为二进制字符串。
     * Bit:位/比特 二进制位 计算机中最小的信息单位    Byte:字节，计算机存储信息的基本单位    1Byte=8Bit
     * 每个字节被转换为一个8位的二进制字符串，字符串中的每一位代表字节中对应位置的位值（0或1）。
     * 字节的转换顺序是从最高位（bit 7）到最低位（bit 0）。
     *
     * @param bytes 输入的字节数组。
     * @return 转换后的二进制字符串。如果输入为null或空数组，则返回空字符串。
     */
    public static String bytes2Bits(final byte[] bytes) {
        if (bytes == null || bytes.length == 0) return "";
        StringBuilder sb = new StringBuilder();
        for (byte aByte : bytes) {
            /*从最高位到最低位遍历每个字节的每一位，并将其转换为字符串形式*/
            for (int j = 7; j >= 0; --j) {
                /*通过位移和位与操作获取当前位的值（0或1），并根据值添加对应的字符到字符串中*/
                sb.append(((aByte >> j) & 0x01) == 0 ? '0' : '1');
            }
        }
        return sb.toString();
    }

    /**
     * 将二进制字符串转换为字节数组。
     * <p>
     * 此方法接受一个表示二进制数据的字符串，其中每个字符代表一个位（bit）。
     * 如果字符串的长度不是8的倍数，它将通过在字符串的前面添加足够的0来填充，以确保长度为8的倍数。
     * 然后，这个字符串被分组为8位一组，每个这样的组被转换为一个字节，并存储在一个字节数组中返回。
     *
     * @param bits 一个表示二进制数据的字符串，其中每个字符代表一个位。
     * @return 一个字节数组，其中每个字节代表8位的二进制数据。
     */
    public static byte[] bits2Bytes(String bits) {
        int lenMod = bits.length() % 8;
        int byteLen = bits.length() / 8;
        // add "0" until length to 8 times
        if (lenMod != 0) {
            for (int i = lenMod; i < 8; i++) {
                bits = "0" + bits;
            }
            byteLen++;
        }
        byte[] bytes = new byte[byteLen];
        for (int i = 0; i < byteLen; ++i) {
            for (int j = 0; j < 8; ++j) {
                bytes[i] <<= 1;
                bytes[i] |= bits.charAt(i * 8 + j) - '0';
            }
        }
        return bytes;
    }

    /**
     * 将字节数组转换为字符数组。
     * 此方法主要用于将字节数据解码为字符数据，假设数据是使用默认字符集编码的。
     *
     * @param bytes 待转换的字节数组，可能为null。
     * @return 转换后的字符数组，如果输入为null或空，则返回null。
     */
    public static char[] bytes2Chars(final byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        int len = bytes.length;
        if (len <= 0) {
            return null;
        }
        char[] chars = new char[len];
        for (int i = 0; i < len; i++) {
            chars[i] = (char) (bytes[i] & 0xff);
        }
        return chars;
    }

    /**
     * 将字符数组转换为字节数组。
     * 此方法主要用于将字符数据转换为字节数据，以便进行进一步的处理或存储。
     *
     * @param chars 输入的字符数组，不为空且长度大于0。
     * @return 对应的字节数组。如果输入为null或空数组，则返回null。
     */
    public static byte[] chars2Bytes(final char[] chars) {
        if (chars == null || chars.length <= 0) {
            return null;
        }
        int len = chars.length;
        byte[] bytes = new byte[len];
        for (int i = 0; i < len; i++) {
            bytes[i] = (byte) (chars[i]);
        }
        return bytes;
    }

    /**
     * 字节数组转换十六进制字符串.
     * <p>e.g. bytes2HexString(new byte[] { 0, (byte) 0xa8 }) returns "00A8"</p>
     *
     * @param bytes The bytes.
     * @return hex string
     */
    public static String bytes2HexString(final byte[] bytes) {
        return bytes2HexString(bytes, true);
    }

    /**
     * 将字节数组转换为十六进制字符串。
     * <p>
     * 此方法提供了将字节数据表示为十六进制字符串的便利方式。它支持生成大写或小写的十六进制字符串，
     * 使得数据的可视化表示更加灵活。
     * </p>
     *
     * @param bytes       要转换的字节数组。如果为null，将返回空字符串。
     * @param isUpperCase 如果为true，生成的十六进制字符串将使用大写字母；否则使用小写字母。
     * @return 转换后的十六进制字符串。如果输入为null或空数组，返回空字符串。
     */
    public static String bytes2HexString(final byte[] bytes, boolean isUpperCase) {
        if (bytes == null) {
            return "";
        }
        char[] hexDigits = isUpperCase ? HEX_DIGITS_UPPER : HEX_DIGITS_LOWER;
        int len = bytes.length;
        if (len <= 0) {
            return "";
        }
        /*预分配一个字符数组，长度为字节数组长度的两倍，因为一个字节可以转换为两个十六进制字符*/
        char[] ret = new char[len << 1];
        for (int i = 0, j = 0; i < len; i++) {
            /*获取字节的高4位，并通过查表转换为对应的十六进制字符*/
            ret[j++] = hexDigits[bytes[i] >> 4 & 0x0f];
            /*获取字节的低4位，并通过查表转换为对应的十六进制字符*/
            ret[j++] = hexDigits[bytes[i] & 0x0f];
        }
        return new String(ret);
    }

    /**
     * 将十六进制字符串转换为字节数组。
     * <p>
     * 此方法解释了为什么需要这个转换函数：处理来自外部的十六进制字符串，将其转换为内部处理的字节数组形式。
     *
     * @param hexString 十六进制字符串，代表要转换的字节序列。
     * @return 转换后的字节数组。
     * @see StringUtils#isSpace(String) 检查字符串是否为空白。
     */
    public static byte[] hexString2Bytes(String hexString) {
        if (StringUtils.isSpace(hexString)) {
            return new byte[0];
        }
        int len = hexString.length();
        if (len % 2 != 0) {
            hexString = "0" + hexString;
            len = len + 1;
        }
        /*将字符串转换为大写，同时转换为数组*/
        char[] hexBytes = hexString.toUpperCase().toCharArray();
        byte[] ret = new byte[len >> 1];
        for (int i = 0; i < len; i += 2) {
            ret[i >> 1] = (byte) (hex2Dec(hexBytes[i]) << 4 | hex2Dec(hexBytes[i + 1]));
        }
        return ret;
    }

    /**
     * 将十六进制字符转换为对应的十进制值。
     * <p>
     * 此方法用于解析十六进制字符串中的每个字符，将其转换为对应的十进制数值。
     * 十六进制字符可以是0-9的数字字符，或者是A-F（大小写不敏感）的字母字符。
     *
     * @param hexChar 十六进制字符，范围包括'0'-'9'、'A'-'F'、'a'-'f'。
     * @return 十六进制字符对应的十进制值。
     * @throws IllegalArgumentException 如果输入的hexChar不是有效的十六进制字符。
     */
    private static int hex2Dec(final char hexChar) {
        if (hexChar >= '0' && hexChar <= '9') {
            return hexChar - '0';
        } else if (hexChar >= 'A' && hexChar <= 'F') {
            return hexChar - 'A' + 10;
        } else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * 将字节数组转换为字符串。
     *
     * @param bytes 待转换的字节数组。
     * @return 转换后的字符串。如果输入的字节数组为null，则返回null。
     */
    public static String bytes2String(final byte[] bytes) {
        return bytes2String(bytes, "");
    }

    /**
     * 将字节数组转换为字符串。
     * 此方法尝试使用指定的字符集来解码字节数组。如果指定的字符集不受支持，
     * 则会回退到默认的字符集。
     *
     * @param bytes       待转换的字节数组。
     * @param charsetName 指定的字符集名称。
     * @return 转换后的字符串。如果输入的字节数组为null，则返回null。
     */
    public static String bytes2String(final byte[] bytes, final String charsetName) {
        if (bytes == null) {
            return null;
        }
        try {
            return new String(bytes, getSafeCharset(charsetName));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return new String(bytes);
        }
    }

    /**
     * 将字符串转换为字节数组。
     *
     * @param string 待转换的字符串。
     * @return 转换后的字节数组。
     */
    public static byte[] string2Bytes(final String string) {
        return string2Bytes(string, "");
    }

    /**
     * 将字符串转换为字节数组。
     * 此方法尝试根据指定的字符集将字符串转换为字节数组。如果指定的字符集不被支持，
     * 则会回退到使用默认字符集。
     *
     * @param string      待转换的字符串。
     * @param charsetName 指定的字符集名称。
     * @return 转换后的字节数组。
     */
    public static byte[] string2Bytes(final String string, final String charsetName) {
        if (string == null) {
            return null;
        }
        try {
            return string.getBytes(getSafeCharset(charsetName));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return string.getBytes();
        }
    }

    /**
     * 将字节数组转换为JSONObject。
     * <p>
     * 此方法尝试将字节数组解析为字符串，然后使用该字符串构造一个JSONObject。如果字节数组不是有效的JSON格式，
     * 或者解析过程中发生其他异常，方法将打印异常堆栈跟踪并返回null。
     *
     * @param bytes 字节数组，代表待转换的JSON字符串。
     * @return 成功解析字节数组为JSONObject时返回相应的JSONObject，否则返回null。
     */
    public static JSONObject bytes2JSONObject(final byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        try {
            return new JSONObject(new String(bytes));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将JSONObject转换为字节数组。
     * 此方法用于将JSON对象序列化为字节数组形式，便于存储或传输。
     *
     * @param jsonObject 待转换的JSONObject对象。
     *                   如果为null，则直接返回null，不进行转换操作。
     * @return 转换后的字节数组，如果输入为null，则返回null。
     */
    public static byte[] jsonObject2Bytes(final JSONObject jsonObject) {
        if (jsonObject == null) {
            return null;
        }
        return jsonObject.toString().getBytes();
    }

    /**
     * 将字节数组转换为JSONArray对象。
     * 此方法尝试将字节数组解析为字符串，然后将该字符串转换为JSONArray对象。如果解析失败，将返回null。
     *
     * @param bytes 字节数组，可能为null。
     * @return 转换后的JSONArray对象，如果输入为null或解析失败，则返回null。
     */
    public static JSONArray bytes2JSONArray(final byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        try {
            return new JSONArray(new String(bytes));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将JSONArray转换为字节数组。
     * 此方法用于将JSON格式的数组字符串转换为字节数组形式，以便于数据传输或存储。
     * 如果输入的JSONArray为null，则直接返回null，不进行转换。
     *
     * @param jsonArray 要转换的JSONArray对象。
     * @return 转换后的字节数组，如果输入为null，则返回null。
     */
    public static byte[] jsonArray2Bytes(final JSONArray jsonArray) {
        if (jsonArray == null) {
            return null;
        }
        return jsonArray.toString().getBytes();
    }

    /**
     * 将字节数组转换为Parcelable对象。
     * 此方法用于从字节数组恢复一个Parcelable对象，字节数组应是先前使用parcelable2Bytes方法创建的。
     *
     * @param bytes   待转换的字节数组，源自一个Parcelable对象。
     * @param creator Parcelable对象的Creator，用于实例化对象。
     * @param <T>     泛型参数，指定要创建的Parcelable对象的类型。
     * @return 从字节数组恢复的Parcelable对象。
     * @see Parcel#unmarshall(byte[], int, int)
     * @see Parcel#setDataPosition(int)
     * @see Parcelable.Creator#createFromParcel(Parcel)
     */
    public static <T> T bytes2Parcelable(final byte[] bytes,
                                         final Parcelable.Creator<T> creator) {
        if (bytes == null) {
            return null;
        }
        Parcel parcel = Parcel.obtain();
        parcel.unmarshall(bytes, 0, bytes.length);
        parcel.setDataPosition(0);
        T result = creator.createFromParcel(parcel);
        parcel.recycle();
        return result;
    }

    /**
     * 将Parcelable对象转换为字节数组。
     * <p>
     * 此方法用于序列化一个Parcelable对象，以便于存储或传输。
     * 它首先获取一个空的Parcel对象，然后将Parcelable对象写入这个Parcel，
     * 接着使用marshall方法将Parcel的内容转换为字节数组，
     * 最后回收这个Parcel对象以供系统重用。
     *
     * @param parcelable 需要转换为字节数组的Parcelable对象。
     * @return 如果parcelable为null，则返回null；否则返回序列化后的字节数组。
     */
    public static byte[] parcelable2Bytes(final Parcelable parcelable) {
        if (parcelable == null) {
            return null;
        }
        Parcel parcel = Parcel.obtain();
        parcelable.writeToParcel(parcel, 0);
        byte[] bytes = parcel.marshall();
        parcel.recycle();
        return bytes;
    }

    /**
     * 将字节数组转换为对象。
     * 该方法尝试将给定的字节数组解码为一个Java对象。字节数组应是先前使用对象的序列化形式创建的。
     *
     * @param bytes 字节数组，代表一个序列化的对象。
     * @return 被反序列化的对象，如果字节数组为null，则返回null。
     */
    public static Object bytes2Object(final byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将Serializable对象转换为字节数组。
     * <p>
     * 此方法用于序列化一个Java对象，将其转换为字节数组形式，便于存储或传输。
     * 如果传入的serializable对象为null，则直接返回null。
     *
     * @param serializable 需要转换的Serializable对象。
     * @return 转换后的字节数组，如果转换失败或对象为null，则返回null。
     */
    public static byte[] serializable2Bytes(final Serializable serializable) {
        if (serializable == null) {
            return null;
        }
        ByteArrayOutputStream baos;
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(baos = new ByteArrayOutputStream());
            oos.writeObject(serializable);
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将字节数组转换为Bitmap对象.
     */
    public static Bitmap bytes2Bitmap(final byte[] bytes) {
        return ImageUtils.bytes2Bitmap(bytes);
    }

    /**
     * Bitmap对象转换为字节数组.
     */
    public static byte[] bitmap2Bytes(final Bitmap bitmap) {
        return ImageUtils.bitmap2Bytes(bitmap);
    }

    /**
     * 将Bitmap对象转换为字节数组。
     * 此方法允许指定图片的压缩格式和质量，以控制转换后的字节数组大小。
     *
     * @param bitmap  需要转换的Bitmap对象。如果为null，则方法直接返回null。
     * @param format  图片的压缩格式。此参数决定了图片以何种方式压缩。
     * @param quality 图片的压缩质量。质量值越低，图片被压缩得越小。
     *                质量值的范围通常为0到100，但具体取决于压缩格式。
     * @return 返回转换后的字节数组。如果输入的bitmap为null，则返回null。
     */
    public static byte[] bitmap2Bytes(final Bitmap bitmap, final Bitmap.CompressFormat format, int quality) {
        return ImageUtils.bitmap2Bytes(bitmap, format, quality);
    }

    /**
     * 字节数组转换为Drawable对象
     */
    public static Drawable bytes2Drawable(final byte[] bytes) {
        return ImageUtils.bytes2Drawable(bytes);
    }

    /**
     * 将Drawable对象转换为字节数组.
     */
    public static byte[] drawable2Bytes(final Drawable drawable) {
        return ImageUtils.drawable2Bytes(drawable);
    }

    /**
     * 将Drawable对象转换为字节数组。
     * 此方法允许指定图片的压缩格式和质量，以控制转换后的字节数组大小。
     *
     * @param drawable 需要转换的Drawable对象。如果为null，则方法直接返回null。
     * @param format   图片的压缩格式。此参数决定了图片以何种方式压缩。
     * @param quality  图片的压缩质量。质量值越低，图片被压缩得越小。
     *                 质量值的范围通常为0到100，但具体取决于压缩格式。
     * @return 返回转换后的字节数组。如果输入的drawable为null，则返回null。
     */
    public static byte[] drawable2Bytes(final Drawable drawable, final Bitmap.CompressFormat format, int quality) {
        return ImageUtils.drawable2Bytes(drawable, format, quality);
    }

    /**
     * 将内存大小从给定单位转换为字节。
     * 该方法提供了将内存大小表达式转换为字节单位的便利方法。内存大小的单位可以是字节、千字节、兆字节或吉字节。
     *
     * @param memorySize 内存大小的值，使用指定的单位。
     * @param unit       内存大小的单位。使用枚举{@link MemoryConstants.Unit}来表示单位，以确保单位的正确性。
     *                   可以是{@link MemoryConstants#BYTE}、{@link MemoryConstants#KB}、
     *                   {@link MemoryConstants#MB}或{@link MemoryConstants#GB}。
     * @return 转换后的内存大小，以字节为单位。如果输入的内存大小小于0，则返回-1，表示转换无效。
     */
    public static long memorySize2Byte(final long memorySize, @MemoryConstants.Unit final int unit) {
        if (memorySize < 0) return -1;
        return memorySize * unit;
    }

    /**
     * 将字节大小转换为指定单位的内存大小。
     * 此方法用于将存储大小的字节值转换为更易读的单位，如KB、MB、GB等。
     *
     * @param byteSize 字节大小，表示待转换的原始存储大小。
     * @param unit     转换目标单位，使用枚举常量指定，如BYTE、KB、MB、GB。
     *                 <ul>
     *                 <li>{@link MemoryConstants#BYTE}</li>
     *                 <li>{@link MemoryConstants#KB}</li>
     *                 <li>{@link MemoryConstants#MB}</li>
     *                 <li>{@link MemoryConstants#GB}</li>
     *                 </ul>
     * @return 转换后的内存大小，如果输入的字节大小小于0，则返回-1表示转换无效。
     */
    public static double byte2MemorySize(final long byteSize, @MemoryConstants.Unit final int unit) {
        if (byteSize < 0) return -1;
        return (double) byteSize / unit;
    }

    /**
     * 将字节大小转换为适合内存大小的字符串表示。
     * 支持定制化精度，并提供对非法参数的检查。
     *
     * @param byteSize 待转换的字节大小，必须大于等于0。
     * @return 格式化后的内存大小字符串，单位分别为字节(B)、千字节(KB)、兆字节(MB)、吉字节(GB)。
     * @throws IllegalArgumentException 如果byteSize或precision小于0，抛出此异常。
     */
    @SuppressLint("DefaultLocale")
    public static String byte2FitMemorySize(final long byteSize) {
        return byte2FitMemorySize(byteSize, 3);
    }

    /**
     * 将字节大小转换为适合内存大小的字符串表示。
     * 支持定制化精度，并提供对非法参数的检查。
     *
     * @param byteSize  待转换的字节大小，必须大于等于0。
     * @param precision 精度，即小数点后的位数，必须大于等于0。
     * @return 格式化后的内存大小字符串，单位分别为字节(B)、千字节(KB)、兆字节(MB)、吉字节(GB)。
     * @throws IllegalArgumentException 如果byteSize或precision小于0，抛出此异常。
     */
    @SuppressLint("DefaultLocale")
    public static String byte2FitMemorySize(final long byteSize, int precision) {
        if (precision < 0) {
            throw new IllegalArgumentException("precision shouldn't be less than zero!");
        }
        if (byteSize < 0) {
            throw new IllegalArgumentException("byteSize shouldn't be less than zero!");
        } else if (byteSize < MemoryConstants.KB) {
            return String.format("%." + precision + "fB", (double) byteSize);
        } else if (byteSize < MemoryConstants.MB) {
            return String.format("%." + precision + "fKB", (double) byteSize / MemoryConstants.KB);
        } else if (byteSize < MemoryConstants.GB) {
            return String.format("%." + precision + "fMB", (double) byteSize / MemoryConstants.MB);
        } else {
            return String.format("%." + precision + "fGB", (double) byteSize / MemoryConstants.GB);
        }
    }


    /**
     * 将时间跨度转换为毫秒。
     * 此方法将给定的时间跨度（以特定时间单位表示）转换为以毫秒为单位的等价值。
     * 这对于在不同时间单位之间进行转换非常有用，例如，将分钟转换为毫秒或将秒转换为毫秒。
     *
     * @param timeSpan 时间跨度，以指定的时间单位计量。
     * @param unit     时间跨度的单位。
     *                 支持的单位有：毫秒（MSEC）、秒（SEC）、分钟（MIN）、小时（HOUR）、天（DAY）。
     *                 使用枚举类型{@link TimeConstants.Unit}来确保单位的合法性。
     * @return 转换后的时间跨度，以毫秒为单位。
     * @see TimeConstants.Unit for supported time units.
     */
    public static long timeSpan2Millis(final long timeSpan, @TimeConstants.Unit final int unit) {
        return timeSpan * unit;
    }

    /**
     * 将毫秒数转换为指定时间单位的时间跨度。
     * 此方法用于将时间长度从毫秒单位转换为更宏观的时间单位，如秒、分钟、小时或天，以便于理解和处理。
     *
     * @param millis 毫秒数，表示要转换的时间长度。
     * @param unit   时间单位，使用枚举常量指定目标时间单位，支持毫秒（MSEC）、秒（SEC）、分钟（MIN）、小时（HOUR）和天（DAY）。
     *               使用枚举类型{@link TimeConstants.Unit}来确保单位的合法性。
     * @return 转换后的时间跨度，以指定的时间单位计。
     * @see TimeConstants 单位常量的定义。
     */
    public static long millis2TimeSpan(final long millis, @TimeConstants.Unit final int unit) {
        return millis / unit;
    }


    /**
     * 将毫秒数转换为更易读的时间跨度字符串。
     * 根据给定的精度，显示天、小时、分钟、秒和毫秒。
     *
     * @param millis    指定的时间跨度，以毫秒为单位。
     * @param precision 精度，表示应显示的最大时间单位。
     *                  例如，precision=2将显示分钟和秒，但不显示毫秒。
     *                  <ul>
     *                  <li>precision = 0, return null</li>
     *                  <li>precision = 1, return 天</li>
     *                  <li>precision = 2, return 天, 小时</li>
     *                  <li>precision = 3, return 天, 小时, 分钟</li>
     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>
     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>
     *                  </ul>
     * @return 时间跨度的字符串表示，例如"3天5小时"。
     * 如果precision小于等于0，返回null。
     * 如果millis为0，返回"0"加上对应的单位。
     * 如果millis为负数，返回带有负号的结果。
     */
    public static String millis2FitTimeSpan(long millis, int precision) {
        return DateUtils.millis2FitTimeSpan(millis, precision);
    }

    /**
     * 将输入流中的数据复制到 ByteArrayOutputStream 中。
     * 这个方法提供了从 InputStream 到 ByteArrayOutputStream 的数据传输功能，
     * 主要用于在不明确输入流具体类型的情况下，将输入流中的数据转换为字节数组输出流。
     *
     * @param is 输入流，从这个流中读取数据。
     * @return 返回 ByteArrayOutputStream，其中包含从输入流读取的所有数据。
     * 如果输入流为 null，或者在复制过程中发生 IOException，则返回 null。
     */
    public static ByteArrayOutputStream input2OutputStream(final InputStream is) {
        if (is == null) {
            return null;
        }
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            byte[] b = new byte[BUFFER_SIZE];
            int len;
            while ((len = is.read(b, 0, BUFFER_SIZE)) != -1) {
                os.write(b, 0, len);
            }
            return os;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将OutputStream的内容转换为InputStream。
     * 这个方法主要用于将操作输出流产生的数据转换为输入流格式，以便于进一步的读取和处理。
     *
     * @param out 输出流，其内容将被转换为输入流。如果为null，则返回null。
     * @return 返回一个ByteArrayInputStream，它读取的数据来自输出流的内容。如果输入参数为null，则返回null。
     */
    public static ByteArrayInputStream output2InputStream(final OutputStream out) {
        if (out == null) {
            return null;
        }
        return new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray());
    }

    /**
     * 将InputStream中的数据转换为字节数组。
     * 这个方法封装了从输入流读取数据并转换为字节数组的逻辑。
     * 它首先将输入流的数据重定向到ByteArrayOutputStream，然后通过调用其toByteArray方法来获取字节数组。
     * 这种方法对于需要将流式数据缓存到内存中以供后续处理的场景非常有用。
     *
     * @param is 输入流，从中读取数据并转换为字节数组。如果输入流为null，则返回null。
     * @return 从输入流读取的数据的字节数组表示。如果输入流为null，或者输入流中没有数据，则可能返回空数组或null。
     */
    public static byte[] inputStream2Bytes(final InputStream is) {
        if (is == null) {
            return null;
        }
        return input2OutputStream(is).toByteArray();
    }

    /**
     * 将字节数组转换为InputStream对象。
     * 这种转换对于将字节数组作为数据源进行读取非常有用，例如在文件上传或数据流处理的场景中。
     *
     * @param bytes 字节数组，转换的目标。
     * @return 返回一个InputStream对象，如果输入的字节数组为空或长度为0，则返回null。
     * 这种设计允许调用者根据返回值是否为null来判断转换是否成功，或者是否有必要进行后续的处理。
     */
    public static InputStream bytes2InputStream(final byte[] bytes) {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        return new ByteArrayInputStream(bytes);
    }

    /**
     * 将OutputStream转换为字节数组。
     * 这个方法假设传入的OutputStream是一个ByteArrayOutputStream的实例。
     * 如果传入的OutputStream为null，那么这个方法将返回null。
     * 主要用于在不关闭OutputStream的前提下，获取其内部的字节数组。
     *
     * @param out 要转换的OutputStream对象。
     * @return 转换后的字节数组，如果输入为null，则返回null。
     */
    public static byte[] outputStream2Bytes(final OutputStream out) {
        if (out == null) {
            return null;
        }
        return ((ByteArrayOutputStream) out).toByteArray();
    }

    /**
     * 将字节数组转换为OutputStream。
     * 这个方法提供了一个方式，将字节数组直接转换为OutputStream对象，以便于进一步的操作或处理。
     *
     * @param bytes 输入的字节数组，如果为null或空数组，则返回null。
     * @return 返回一个ByteArrayOutputStream，包含输入字节数组的数据，如果转换失败或输入为null/空数组，则返回null。
     */
    public static OutputStream bytes2OutputStream(final byte[] bytes) {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        ByteArrayOutputStream os = null;
        try {
            os = new ByteArrayOutputStream();
            os.write(bytes);
            return os;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将InputStream转换为字符串。
     * 此方法尝试将输入流中的字节转换为指定字符集的字符串。如果字符集不受支持，
     * 则会回退到使用安全字符集或默认字符集。
     *
     * @param is          输入流，将被转换为字符串。
     * @param charsetName 指定的字符集名称，用于将字节转换为字符串。
     * @return 转换后的字符串，如果输入流或字符集无效，则返回空字符串。
     */
    public static String inputStream2String(final InputStream is, final String charsetName) {
        if (is == null) {
            return "";
        }
        try {
            ByteArrayOutputStream baos = input2OutputStream(is);
            if (baos == null) {
                return "";
            }
            return baos.toString(getSafeCharset(charsetName));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 将字符串转换为输入流。
     * 此方法用于将给定的字符串转换为一个 ByteArrayInputStream，以便于将字符串作为数据源进行读取。
     *
     * @param string      待转换的字符串。
     * @param charsetName 字符串的字符集名称。如果字符集不受支持，则会回退到使用默认字符集。
     * @return 返回一个 ByteArrayInputStream，该输入流可以用于读取转换后的字符串数据。
     * 如果输入的字符串为null，则返回null。
     */
    public static InputStream string2InputStream(final String string, final String charsetName) {
        if (string == null) {
            return null;
        }
        try {
            return new ByteArrayInputStream(string.getBytes(getSafeCharset(charsetName)));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将OutputStream中的数据转换为字符串。
     * 此方法尝试将输出流中的字节数据解析为字符串，使用指定的字符集。如果指定的字符集不被支持，
     * 则会回退到使用安全字符集或默认字符集。
     *
     * @param out         输出流，数据将从此流中读取并转换为字符串。
     * @param charsetName 指定的字符集名称，用于解析字节数据。
     * @return 转换后的字符串，如果输出流为空或字符集不支持，则返回空字符串。
     */
    public static String outputStream2String(final OutputStream out, final String charsetName) {
        if (out == null) {
            return "";
        }
        try {
            return new String(outputStream2Bytes(out), getSafeCharset(charsetName));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 将字符串转换为输出流。
     * 此方法主要用于将字符串数据写入到输出流中，便于后续进行网络传输或文件写入等操作。
     *
     * @param string      待转换的字符串内容。
     * @param charsetName 字符串的字符集名称，用于指定字符串的编码格式。
     * @return 返回转换后的输出流，如果转换失败则返回null。
     */
    public static OutputStream string2OutputStream(final String string, final String charsetName) {
        if (string == null) {
            return null;
        }
        try {
            return bytes2OutputStream(string.getBytes(getSafeCharset(charsetName)));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将InputStream转换为字符串列表。
     * 每行作为一个字符串元素，行末符被去除。
     *
     * @param is 要转换的InputStream对象，不为空。
     * @return 包含输入流所有行的字符串列表，如果发生IO异常，则返回null。
     */
    public static List<String> inputStream2Lines(final InputStream is) {
        return inputStream2Lines(is, "");
    }

    /**
     * 将InputStream转换为字符串列表。
     * 每行作为一个字符串元素，行末符被去除。
     *
     * @param is          要转换的InputStream对象，不为空。
     * @param charsetName 指定的字符集名称，如果为空或无效，则使用默认字符集。
     * @return 包含输入流所有行的字符串列表，如果发生IO异常，则返回null。
     */
    public static List<String> inputStream2Lines(final InputStream is,
                                                 final String charsetName) {
        BufferedReader reader = null;
        try {
            List<String> list = new ArrayList<>();
            reader = new BufferedReader(new InputStreamReader(is, getSafeCharset(charsetName)));
            String line;
            /*逐行读取，直到文件末尾*/
            while ((line = reader.readLine()) != null) {
                list.add(line);
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将Drawable对象转换为Bitmap。
     *
     * @param drawable 要转换的Drawable对象。
     * @return 转换后的Bitmap对象，如果转换失败或输入为null，则返回null。
     */
    public static Bitmap drawable2Bitmap(final Drawable drawable) {
        return ImageUtils.drawable2Bitmap(drawable);
    }

    /**
     * 将Bitmap转换为Drawable对象。
     */
    public static Drawable bitmap2Drawable(final Bitmap bitmap) {
        return ImageUtils.bitmap2Drawable(bitmap);
    }

    /**
     * 将View转换为Bitmap。
     */
    public static Bitmap view2Bitmap(final View view) {
        return ImageUtils.view2Bitmap(view);
    }

    /**
     * 将dp值转换为px值
     */
    public static int dp2px(final float dpValue) {
        return SizeUtils.dp2px(dpValue);
    }

    /**
     * 将px值转换为dp值
     */
    public static int px2dp(final float pxValue) {
        return SizeUtils.px2dp(pxValue);
    }

    /**
     * 将sp值转换为px值
     */
    public static int sp2px(final float spValue) {
        return SizeUtils.sp2px(spValue);
    }

    /**
     * 将px值转换为sp值
     */
    public static int px2sp(final float pxValue) {
        return SizeUtils.px2sp(pxValue);
    }

    /**
     * 获取安全字符集。
     * 该方法用于确定并返回一个字符集，优先使用传入的字符集，如果传入的字符集不合法或不被支持，
     * 则默认返回UTF-8字符集。这样设计是为了确保程序在处理字符集时的兼容性和安全性。
     *
     * @param charsetName 指定的字符集名称
     * @return 返回有效的字符集名称，如果指定的字符集不合法或不被支持，则返回UTF-8
     */
    private static String getSafeCharset(String charsetName) {
        String cn = charsetName;
        if (StringUtils.isSpace(charsetName) || !Charset.isSupported(charsetName)) {
            cn = "UTF-8";
        }
        return cn;
    }
}
